home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 July: Mac OS SDK / Dev.CD Jul 96 SDK / Dev.CD Jul 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc Development Framework / ODFDev / ODF / OS / FWGraphx / Sources / PRTxtBuf.cpp < prev    next >
Encoding:
Text File  |  1996-04-25  |  11.9 KB  |  481 lines  |  [TEXT/MPS ]

  1. //========================================================================================
  2. //
  3. //    File:                PRTxtBuf.h
  4. //    Release Version:    $ ODF 1 $
  5. //
  6. //    Modified by MEB to support non-single-byte characters
  7. //
  8. //    Copyright:    (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  9. //
  10. //========================================================================================
  11.  
  12. #include "FWOS.hpp"
  13.  
  14. #ifndef PRTXTBUF_H
  15. #include "PRTxtBuf.h"
  16. #endif
  17.  
  18. #ifndef FWMEMORY_H
  19. #include "FWMemory.h"
  20. #endif
  21.  
  22. #ifndef FWSOMENV_H
  23. #include "FWSOMEnv.h"
  24. #endif
  25.  
  26. //========================================================================================
  27. //    RunTime Info
  28. //========================================================================================
  29.  
  30. #ifdef FW_BUILD_MAC
  31. #pragma segment FWGraphx_TextBuf
  32. #endif
  33.  
  34. FW_DEFINE_AUTO(FW_CPrivTextBuffer)
  35.  
  36. //========================================================================================
  37. // CLASS FW_CPrivTextBuffer
  38. //========================================================================================
  39.  
  40. //----------------------------------------------------------------------------------------
  41. // FW_CPrivTextBuffer::FW_CPrivTextBuffer
  42. //----------------------------------------------------------------------------------------
  43.  
  44. FW_CPrivTextBuffer::FW_CPrivTextBuffer(FW_HString string) :
  45.     fString(string),
  46.     fReader(NULL),
  47.     fCurrentLine(NULL),
  48.     fCurRun(NULL),
  49.     fCurRunIsLast(FALSE),
  50.     fIsDone(FALSE),
  51.     fIsLastLine(FALSE),
  52.     fDynamicBufferBase(NULL),
  53.     fUsingFixedBuffer(TRUE)
  54. {
  55.     Advance();
  56.     FW_END_CONSTRUCTOR
  57. }
  58.     
  59. //----------------------------------------------------------------------------------------
  60. // FW_CPrivTextBuffer::FW_CPrivTextBuffer
  61. //----------------------------------------------------------------------------------------
  62.  
  63. FW_CPrivTextBuffer::FW_CPrivTextBuffer(FW_HTextReader reader) :
  64.     fString(NULL),
  65.     fReader(reader),
  66.     fCurrentLine(NULL),
  67.     fCurRun(NULL),
  68.     fCurRunIsLast(NULL),
  69.     fIsDone(FALSE),
  70.     fIsLastLine(FALSE),
  71.     fUsingFixedBuffer(TRUE),
  72.     fDynamicBufferBase(NULL)
  73. {
  74.     Advance();
  75.     FW_END_CONSTRUCTOR
  76. }
  77.  
  78. //----------------------------------------------------------------------------------------
  79. // FW_CPrivTextBuffer::~FW_CPrivTextBuffer
  80. //----------------------------------------------------------------------------------------
  81.  
  82. FW_CPrivTextBuffer::~FW_CPrivTextBuffer()
  83. {
  84.     FW_START_DESTRUCTOR
  85.     FW_CMemoryManager::FreeBlock(fDynamicBufferBase);
  86. }
  87.  
  88. //----------------------------------------------------------------------------------------
  89. // FW_CPrivTextBuffer::IsDone
  90. //----------------------------------------------------------------------------------------
  91.  
  92. FW_Boolean FW_CPrivTextBuffer::IsDone() const
  93. {
  94.     return fIsDone;
  95. }
  96.  
  97. //----------------------------------------------------------------------------------------
  98. // FW_CPrivTextBuffer::Advance
  99. //----------------------------------------------------------------------------------------
  100.  
  101. void FW_CPrivTextBuffer::Advance()
  102. {
  103.     // the user should not call this method after we told hir we're done
  104.     FW_ASSERT(!fIsDone);
  105.  
  106.     // get the next run if we need to
  107.     if (fCurRun == NULL)
  108.         if (!GetNextRun())
  109.         {
  110.             fIsDone = TRUE;
  111.             return;
  112.         }
  113.  
  114.     // search for newline in the current run
  115.     char* p = FindRunNewLine();
  116.  
  117.     if (p != NULL)
  118.     {
  119.         // Found a newline
  120.         fCurrentLine         =     fCurRunPos;
  121.         fCurrentLineLength    =     p - fCurRunPos + 1;
  122.         fCurRunPos            =    p + 1;
  123.         return;
  124.     }
  125.  
  126.     if (fCurRunIsLast)
  127.     {
  128.         // No newline, last run - use the remainder of the run
  129.         fCurrentLine        =     fCurRunPos;
  130.         fCurrentLineLength    =     fCurRunLen - (fCurRunPos - fCurRun);
  131.         fIsLastLine            =    TRUE;
  132.         fIsDone                =     fCurrentLineLength == 0;
  133.         return;
  134.     }
  135.  
  136.     // No newline, not the last run - consume the run
  137.     ResetBuffer(fCurRunLen - (fCurRunPos - fCurRun));
  138.     AppendToBuffer(fCurRunPos, fCurRunLen - (fCurRunPos - fCurRun));
  139.  
  140.     // Keep going through the runs until we find a newline or the show is over
  141.     while (GetNextRun())
  142.     {
  143.         char* p = FindRunNewLine();
  144.         FW_ByteCount len = p == NULL ? fCurRunLen : p - fCurRunPos + 1;
  145.  
  146.         AppendToBuffer(fCurRunPos, len);
  147.         fCurRunPos += len;
  148.  
  149.         if (p != NULL)        // Found a newline
  150.             break;
  151.  
  152.         if (fCurRunIsLast)    // This was the last run, bail out
  153.         {
  154.             fIsLastLine = TRUE;
  155.             break;
  156.         }
  157.     }
  158. }
  159.  
  160. //----------------------------------------------------------------------------------------
  161. // FW_CPrivTextBuffer::FindRunNewLine
  162. //----------------------------------------------------------------------------------------
  163.  
  164. char*    FW_CPrivTextBuffer::FindRunNewLine()
  165. {
  166.     char* runLimit = fCurRun + fCurRunLen, *p = fCurRunPos;
  167.     while (p < runLimit && *p != '\n')
  168.         ++ p;
  169.  
  170.     return (p < runLimit) ? p : NULL;
  171. }
  172.  
  173. //----------------------------------------------------------------------------------------
  174. // FW_CPrivTextBuffer::GetCurrentLine
  175. //----------------------------------------------------------------------------------------
  176.  
  177. void FW_CPrivTextBuffer::GetCurrentLine(const char*& pLine, FW_ByteCount& len)
  178. {
  179.     pLine    =    fCurrentLine;
  180.     len        =    fCurrentLineLength;
  181.     
  182.     if (fIsLastLine)
  183.         fIsDone = TRUE;
  184. }
  185.  
  186. //----------------------------------------------------------------------------------------
  187. // FW_CPrivTextBuffer::ResetBuffer
  188. //----------------------------------------------------------------------------------------
  189.  
  190. void FW_CPrivTextBuffer::ResetBuffer(FW_ByteCount size)
  191. {
  192.     if(size <= kBufferSize)
  193.     {
  194.         // the fixed buffer is large enough; use it
  195.         FW_CMemoryManager::FreeBlock(fDynamicBufferBase);
  196.         fDynamicBufferBase        = NULL;
  197.         fCurrentBufferBase        = fFixedBuffer;
  198.         fCurrentBufferSize        = kBufferSize;
  199.     }
  200.     else
  201.     {
  202.         // the fixed buffer is too small; allocate a dynamic buffer
  203.         fDynamicBufferBase        = (char*) FW_CMemoryManager::AllocateBlock(kBufferSize);
  204.         fDynamicBufferSize        = kBufferSize;
  205.         fCurrentBufferBase        = fDynamicBufferBase;
  206.         fCurrentBufferSize        = fDynamicBufferSize;
  207.     }
  208.  
  209.     fCurrentLineLength    = 0;
  210.     fCurrentLine        = fCurrentBufferBase;
  211. }
  212.  
  213. //----------------------------------------------------------------------------------------
  214. // FW_CPrivTextBuffer::AppendToBuffer
  215. //----------------------------------------------------------------------------------------
  216.  
  217. void FW_CPrivTextBuffer::AppendToBuffer(const char* pch, FW_ByteCount cb)
  218. {
  219.     if(fCurrentLineLength + cb <= fCurrentBufferSize)
  220.     {
  221.         // the buffer is still large enough, just stick the character in
  222.         FW_PrimitiveCopyMemory(pch, fCurrentLine + fCurrentLineLength, cb);
  223.     }
  224.     else
  225.     {
  226.         // the buffer became too small
  227.         if(fDynamicBufferBase == NULL)
  228.         {
  229.             // we are switching from a static to a dynamic buffer
  230.             fDynamicBufferBase        = (char*) FW_CMemoryManager::AllocateBlock(2 * kBufferSize);
  231.             fDynamicBufferSize        = 2 * kBufferSize;
  232.             fCurrentBufferBase        = fDynamicBufferBase;
  233.             fCurrentBufferSize        = fDynamicBufferSize;
  234.             
  235.             // copy the current contents into the dynamic buffer
  236.             ::FW_PrimitiveCopyMemory(fFixedBuffer, fDynamicBufferBase, kBufferSize);
  237.         }
  238.         else
  239.         {
  240.             // just reallocate the dynamic buffer
  241.             fDynamicBufferSize        += kBufferSize;
  242.             fDynamicBufferBase        = (char*) FW_CMemoryManager::ResizeBlock(fDynamicBufferBase, fDynamicBufferSize);
  243.             fCurrentBufferBase        = fDynamicBufferBase;
  244.             fCurrentBufferSize        = fDynamicBufferSize;
  245.         }
  246.     }
  247.  
  248.     fCurrentLine        = fCurrentBufferBase;
  249.     fCurrentLineLength += cb;
  250. }
  251.  
  252. //----------------------------------------------------------------------------------------
  253. // FW_CPrivTextBuffer::GetNextRun
  254. //----------------------------------------------------------------------------------------
  255.  
  256. FW_Boolean FW_CPrivTextBuffer::GetNextRun()
  257. {
  258.     FW_ASSERT(!fCurRunIsLast);        // No advancing beyond last run!
  259.     if (fString != NULL)
  260.     {
  261.         fCurRun            = (char*)FW_PrivString_RevealBuffer(fString);
  262.         fCurRunLen        = FW_PrivString_GetByteLength(fString);
  263.         fCurRunIsLast    = TRUE;
  264.     }
  265.     else
  266.     {
  267.         FW_PrivTextReader_PeekRunAhead(fReader, (const char**) &fCurRun, &fCurRunLen);
  268.  
  269.         FW_ASSERT(fCurRun != NULL);
  270.         FW_ASSERT(fCurRunLen != 0);
  271.  
  272.         //???JEL: I added the FW_SOMEnvironment and FW_FailOnEvError, but I'm not
  273.         // certain this is a complete error handling stategy for this function.
  274.         FW_SOMEnvironment ev;
  275.         FW_PrivTextReader_Advance(fReader, ev, fCurRunLen);
  276.         FW_FailOnEvError(ev);
  277.  
  278.         fCurRunIsLast = FW_PrivTextReader_GetPosition(fReader)
  279.             >= FW_PrivTextReader_GetByteLength(fReader);
  280.     }
  281.  
  282.     fCurRunPos = fCurRun;
  283.     return fCurRunLen != 0;
  284. }
  285.  
  286. //========================================================================================
  287. // Global Method FW_PrivGetStringSegment
  288. //========================================================================================
  289. //
  290. //      Used by word-wrapping text measurement and drawing methods
  291. //
  292. //      str         - the string we're wrapping
  293. //      len         - length of the string
  294. //      segStart    - start of the segment that should be drawn
  295. //      segLen      - length of the segement that should be drawn
  296. //      maxWidth    - max width that the string can be when drawn
  297. //      actualWidth - the actual width of the string
  298. //      wordWrap    - should we try to wrap text or just draw it in one piece
  299. //        wordBreak   - if the first word doesn't fit, should we break it?
  300.  
  301. FW_Boolean    FW_PrivGetStringSegment(
  302.     ODPlatformCanvas        platformCanvas,
  303.     FW_Boolean&                bCalledBefore,
  304.     const char*                str,
  305.     FW_ByteCount            len,
  306.     FW_BytePosition&        segStart,
  307.     FW_ByteCount&            segLen,
  308.     FW_PlatformCoordinate    maxWidth,
  309.     FW_PlatformCoordinate&    actualWidth,
  310.     FW_Boolean                wordWrap,
  311.     FW_Boolean                wordBreak)
  312. {
  313.     enum
  314.     {
  315.         kSpace = ' ',
  316. #ifdef FW_BUILD_WIN
  317.         kCR = '\r',
  318. #endif
  319.         kLF = '\n'
  320.     };
  321.  
  322.     segStart = 0;
  323.     segLen = 0;
  324.  
  325.     // if we encountered an end-line during the previous call, "str" will point to a newline char
  326.     if (bCalledBefore)
  327.     {
  328. #ifdef FW_BUILD_WIN
  329.         if (*str == kCR && len > 0)
  330.         {
  331.             str++;
  332.             segStart++;
  333.             len--;
  334.         }
  335. #endif
  336.         if (*str == kLF && len > 0)
  337.         {
  338.             str++;
  339.             segStart++;
  340.             len--;
  341.         }
  342.     }
  343.     bCalledBefore = TRUE;
  344.  
  345.     // skip the leading spaces
  346.     while (*str == kSpace && len > 0)
  347.     {
  348.         str++;
  349.         segStart++;
  350.         len--;
  351.     }
  352.  
  353.     if (len == 0)
  354.         return FALSE;
  355.  
  356. #ifdef FW_BUILD_WIN
  357.     SIZE size;
  358. #endif
  359.  
  360.     if (!wordWrap)
  361.     {
  362.         // return the whole string in one chunk
  363.         segLen = len;
  364. #ifdef FW_BUILD_WIN
  365.         ::GetTextExtentPoint(platformCanvas, str, len, &size);
  366.         actualWidth = size.cx;
  367. #endif
  368. #ifdef FW_BUILD_MAC
  369. FW_UNUSED(platformCanvas);
  370.         actualWidth = ::TextWidth(Ptr(str), 0, len);
  371. #endif
  372.     }
  373.     else
  374.     {
  375.         // break the string into chunks
  376.         FW_PlatformCoordinate accumWidth = 0;
  377.         FW_PlatformCoordinate newWidth;
  378.         FW_PlatformCoordinate wordWidth;
  379.         const char* s = str;
  380.         const char* wordEnd = NULL;
  381.  
  382.         segLen = 0;
  383.  
  384.         while (len > 0)
  385.         {
  386.             // check if we are at the end of a word
  387.             if (*s == kSpace)
  388.             {
  389.                 if (*(s - 1) != kSpace)
  390.                 {
  391.                     wordEnd = s;
  392.                     wordWidth = accumWidth;
  393.                 }
  394.             }
  395.  
  396.             // check if we have a line terminator
  397. #ifdef FW_BUILD_WIN
  398.             if (*s == kCR || *s == kLF)
  399. #endif
  400. #ifdef FW_BUILD_MAC
  401.             if (*s == kLF)
  402. #endif
  403.             {
  404.                 wordEnd = s;
  405.                 wordWidth = accumWidth;
  406.                 break;
  407.             }
  408.  
  409.             // add the current char width
  410. #ifdef FW_BUILD_WIN
  411.             ::GetTextExtentPoint(platformCanvas, str, s - str + 1, &size);
  412.             newWidth = size.cx;
  413. #endif
  414. #ifdef FW_BUILD_MAC
  415.             newWidth = ::TextWidth(Ptr(str), 0, s - str + 1);
  416. #endif
  417.             if (newWidth > maxWidth)
  418.                 break;
  419.  
  420.             accumWidth = newWidth;
  421.  
  422.             // advance to the next character
  423.             s++;
  424.             len--;
  425.         }
  426.  
  427.         // the whole string fit just fine
  428.         if (len == 0)
  429.         {
  430.             wordEnd = s;
  431.             wordWidth = accumWidth;
  432.         }
  433.  
  434.         // the first character was too wide
  435.         if (s == str)
  436.         {
  437.             // ... but it was the first character, so draw it anyways!
  438.             s++;
  439.             len--;
  440. #ifdef FW_BUILD_WIN
  441.             ::GetTextExtentPoint(platformCanvas, str, 1, &size);
  442.             newWidth = size.cx;
  443. #endif
  444.  
  445. #ifdef FW_BUILD_MAC
  446.             newWidth = ::TextWidth(Ptr(str), 0, 1);
  447. #endif
  448.         }
  449.  
  450.         // the first word didn't fit...
  451.         if (wordEnd == NULL)
  452.         {
  453.             if (!wordBreak)
  454.             {
  455.                 // it's not ok to break the first word, keep looking
  456.                 while (len > 0 && *s != ' ')
  457.                 {
  458. #ifdef FW_BUILD_WIN
  459.                     SIZE size;
  460.                     ::GetTextExtentPoint(platformCanvas, str, s - str + 1, &size);
  461.                     accumWidth = size.cx;
  462. #endif
  463. #ifdef FW_BUILD_MAC
  464.                     accumWidth = ::TextWidth(Ptr(str), 0, s - str + 1);
  465. #endif
  466.                     s++;
  467.                     len--;
  468.                 }
  469.             }
  470.  
  471.             wordWidth = accumWidth;
  472.             wordEnd = s;
  473.         }
  474.  
  475.         segLen = wordEnd - str;
  476.         actualWidth = wordWidth;
  477.     }
  478.  
  479.     return TRUE;
  480. }
  481.